package com.igeek.ch02;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Member;
import java.util.Arrays;

/**
 * @package com.igeek.ch02
 * @Description 切面类
 * @Author LittlePunk [296526219@qq.ccom]
 * @Version 2022.1
 * @Date 2023/11/15 14:47
 */

    /*
    声明日志切面：
    * @Component  注册到IOC容器
    * @Aspect  声明当前类时一个切面
    * @Order  决定切面的优先级  值越小优先级越高  越先执行
    *
    * */

/**
 * spring-aop使用步骤
 * 1.在pom.xml中添加 spring-aop  org.aspectj 两个依赖
 * 2.在配置类 扫描包注解@ComponentScan + 开启AOP动态代理@EnableAspectJAutoProxy
 * 3.接口的实现类 添加 @Component 注入到IOC容器中
 * 4.编写一个切面类 @Component+@Aspect
 * 5.在切面中编写方法（通知）@Before前置通知 @After 后置通知...
 * 6.在通知方法中，通过切点表达式execution 确定作用在哪一个类的哪一个方法的位置上
 * 7.在通知方法中，可以通过JoinPoint连接点 获取目标方法的方法名称和形参信息
 */
@Component
@Aspect
@Order
public class LogAspect {
    @Pointcut("execution(public *  com.igeek.ch02.ICount.*(..))")
    public void pt(){}
    /**
     * 前置通知
     * @param joinPoint  连接点  获取到目标方法的方法名  方法的形参列表  (无法手动调用目标方法)
     *
     * 切点表达式:
     *  execution(public int com.igeek.ch02.ICount.*(int,int))作用于当前接口的所有方法
     *  execution(public int com.igeek.ch02.ICount.*(..)) 形参列表时可变参数
     */
    //@Before("pt()")
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("beforeAdvice method"+joinPoint.getSignature().getName()+
                ",args="+ Arrays.toString(joinPoint.getArgs()));
    }
    //环绕通知

    /**
     *
     * @param pj  ProceedingJoinPoint extends JointPoint
     *            ProceedingJoinPoint 不仅可以获取目标方法的信息，还可以执行目标方法proceed
     */
    @Around("pt()")
    public Object aroundAdvice(ProceedingJoinPoint pj){
        //获取方法名
        String methodName = pj.getSignature().getName();
        //获取参数列表
        Object[] args = pj.getArgs();
        //开启日志追踪
        System.out.println("@Around 日志开始追踪 method="+methodName+",Args="+Arrays.toString(args));

        //执行目标方法
        Object result =null ;
        try {
            result = pj.proceed();
            System.out.println("@Around 获取执行结果 method="+methodName+",result="+result);
        } catch (Throwable e) {
            System.out.println("@Around 执行方法出现异常 method="+methodName+",e"+e.getMessage());
            throw new RuntimeException(e);
        }
        System.out.println("@Around  结束日志追踪 method="+methodName+",result"+result);
        return result;
    }
    //后置通知
    //@After("pt()")
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("afterAdvice method"+joinPoint.getSignature().getName()+
                ",args="+ Arrays.toString(joinPoint.getArgs()));
    }


    /**
     * 返回通知
     * @param joinPoint
     * @param result 接收方法的执行结果、名称必须和@AfterReturning注解的returning一致
     *
     * @AfterReturning
     * 1.value属性：声明切点表达式
     * 2.returning属性: 值必须和形参列表名称一致  接收方法的执行结果
     */
    //@AfterReturning(value="pt()",returning = "result")
    public void returnAdvice(JoinPoint joinPoint,Object result){
        System.out.println("returnAdvice method="+ joinPoint.getSignature().getName()+
                ",result="+result);
    }
    //异常通知
    //@AfterThrowing(value = "pt()",throwing = "e")
    public void AfterThrowingAdvice(JoinPoint joinPoint,Exception e){
        System.out.println("throwableAdvice method="+joinPoint.getSignature().getName()
                +",发生了异常ex="+e.getMessage());
    }
}
